--[[
	Copyright (c) 2012 Eyal Shilony

	Permission is hereby granted, free of charge, to any person obtaining
	a copy of this software and associated documentation files (the
	"Software"), to deal in the Software without restriction, including
	without limitation the rights to use, copy, modify, merge, publish,
	distribute, sublicense, and/or sell copies of the Software, and to
	permit persons to whom the Software is furnished to do so, subject to
	the following conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
	LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
	OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
	WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
	
	-- A library that provides set of predefined shortcuts to features that are available in the game.
]]

assert(LibStub, "LibShortcuts-4.0 requires LibStub")

local lib, minor = LibStub:NewLibrary("LibShortcuts-4.0", 0)
if not lib then return end
minor = minor or 0

local GetUIPanel = GetUIPanel
local HideUIPanel = HideUIPanel
local ShowUIPanel = ShowUIPanel
local UIPanelWindows = UIPanelWindows

--[[ LOCALIZATION ]]

local CALENDAR = "Calendar"
local CHARACTER_MSG = "|cffFFFF00The shortcut '%s' cannot run because the character level is too low.|r"
local COMBAT_MSG = "|cffFFFF00The shortcut '%s' cannot run in combat due to Blizzard in-combat restrictions.|r"

--[[ MANAGED FRAMES ]]

-- The following frames cannot be opened or hidden while in-combat.
RestrictedFrames = {
	--["PaperDollFrame"] = true,
	--["CharacterFrame"] = true,
	["SpellBookFrame"] = true,
}

-- When we click on a shortcut, make sure to hide the GameMenuFrame for the following frames. 
GameMenu_OptionFrames = {
	["VideoOptionsFrame"] = true,
	["AudioOptionsFrame"] = true,
	["InterfaceOptionsFrame"] = true,
	["MacOptionsFrame"] = true,
	["KeyBindingFrame"] = true,
}

-- Load these beforehand when a shortcut is executed.
LoD_AddonFrames = {
	["PlayerTalentFrame"] = TalentFrame_LoadUI,
	["AchievementFrame"] = AchievementFrame_LoadUI,
	["CalendarFrame"] = Calendar_LoadUI,
	["KeyBindingFrame"] = KeyBindingFrame_LoadUI,
	["MacroFrame"] = MacroFrame_LoadUI,
	["TimeManagerFrame"] = TimeManager_LoadUI,
	["GuildFrame"] = GuildFrame_LoadUI,
}

--[[ PREDEFINED SHORTCUTS ]]

--[[
	name - (string) The name of the shortcut.
	title - (string) The title of the shortcut.
	shorthand - (string) An optional shorthand for the shortcut.
	frame - (string) The frame that the library should manage while running the shortcut.
	level - (number) The minimum level of the player required to run the shortcut.
	func - (function) The function (handler) to execute.
]]
local shortcuts = {
	{
		name = "character",
		title = CHARACTER_BUTTON,
		shorthand = "char",
		frame = "CharacterFrame",
		func = function() ToggleCharacter("PaperDollFrame") end,
	},
	{
		name = "spellbook",
		title = SPELLBOOK_ABILITIES_BUTTON,
		shorthand = "sb",
		frame = "SpellBookFrame",
		func = function() ToggleFrame(SpellBookFrame) end,
	},
	{
		name = "talents",
		title = TALENTS_BUTTON,
		shorthand = "tal",
		frame = "PlayerTalentFrame",
		level = SHOW_TALENT_LEVEL,
		func = function() ToggleTalentFrame() end,
	},
	{
		name = "achievements",
		title = ACHIEVEMENT_BUTTON,
		shorthand = "achi",
		frame = "AchievementFrame",
		func = function() ToggleAchievementFrame() end,
	},
	{
		name = "calendar",
		title = CALENDAR,
		shorthand = "cal",
		frame = "CalendarFrame",
		func = function() ToggleCalendar() end,
	},
	{
		name = "questlog",
		title = QUESTLOG_BUTTON,
		shorthand = "ql",
		frame = "QuestLogFrame",
		func = function() ToggleFrame(QuestLogFrame) end,
	},
	{
		name = "social",
		title = SOCIAL_BUTTON,
		shorthand = "soc",
		frame = "FriendsFrame",
		func = function() ToggleFriendsFrame() end,
	},
	{
		name = "pvp",
		title = PLAYER_V_PLAYER,
		frame = "PVPFrame",
		level = SHOW_PVP_LEVEL,
		func = function() TogglePVPFrame() end,
	},
	{
		name = "lfd",
		title = DUNGEONS_BUTTON,
		frame = "LFDParentFrame",
		level = SHOW_LFD_LEVEL,
		func = function() ToggleLFDParentFrame() end,
	},
	{
		name = "lfr",
		title = RAID_FINDER,
		frame = "LFRParentFrame",
		level = SHOW_LFD_LEVEL,
		func = function() ToggleRaidFrame() end,
	},
	{
		name = "guild",
		title = GUILD,
		frame = "GuildFrame",
		func = function() ToggleGuildFrame() end,
	},
	{
		name = "backpack",
		title = BACKPACK_TOOLTIP,
		func = function() 
			if IsModifierKeyDown() then
				ToggleAllBags()
			else
				ToggleBackpack()
			end
		end,
	},
	{
		name = "keyring",
		title = KEYRING,
		func = function() 
			ToggleKeyRing()
		end,
	},
	{
		name = "time",
		title = TIMEMANAGER_TITLE,
		frame = "TimeManagerFrame",
		func = function() ToggleTimeManager() end,
	},
	{
		name = "help",
		title = HELP_BUTTON,
		frame = "HelpFrame",
		func = function() ToggleHelpFrame() end,
	},
	{
		name = "options",
		title = SYSTEMOPTIONS_MENU,
		shorthand = "op",
		frame = "VideoOptionsFrame",
		func = function()
			if not VideoOptionsFrame:IsVisible() then
				ShowUIPanel(VideoOptionsFrame)
				VideoOptionsFrame.lastFrame = GameMenuFrame
				VideoOptionsFrame.ignoreMenu = true
			else
				HideUIPanel(VideoOptionsFrame)
			end
		end,
	},
	{
		name = "interface",
		title = UIOPTIONS_MENU,
		shorthand = "ui",
		frame = "InterfaceOptionsFrame",
		func = function()
			if not InterfaceOptionsFrame:IsVisible() then
				ShowUIPanel(InterfaceOptionsFrame)
				InterfaceOptionsFrame.lastFrame = GameMenuFrame
				InterfaceOptionsFrame.ignoreMenu = true
			else
				HideUIPanel(InterfaceOptionsFrame)
			end
		end,
	},
	{
		name = "mac",
		title = MAC_OPTIONS,
		frame = "MacOptionsFrame",
		func = function()
			if not MacOptionsFrame:IsVisible() then
				ShowUIPanel(MacOptionsFrame)
				MacOptionsFrame.ignoreMenu = true
			else
				HideUIPanel(MacOptionsFrame)
			end
		end,
	},
	{
		name = "bindings",
		title = KEY_BINDINGS,
		shorthand = "binds",
		frame = "KeyBindingFrame",
		func = function()
			if not KeyBindingFrame:IsVisible() then
				ShowUIPanel(KeyBindingFrame)
				KeyBindingFrame.ignoreMenu = true
			else
				HideUIPanel(KeyBindingFrame)
			end
		end,
	},
	{
		name = "macro",
		title = MACROS,
		frame = "MacroFrame",
		func = function()
			if not MacroFrame:IsVisible() then
				ShowUIPanel(MacroFrame)
			else
				HideUIPanel(MacroFrame)
			end
		end,
	}
}

-- if we're not using a mac client, remove the shortcut.
if IsMacClient() ~= 1 then
	for i, v in ipairs(shortcuts) do
		if v.name == "mac" then
			table.remove(shortcuts, i) 
			break
		end
	end
end

local function comp(a, b)
	return a.title:lower() < b.title:lower()
end

table.sort(shortcuts, comp)

local num = 0
local titles = { }
for _, v in ipairs(shortcuts) do
	num = num + 1
	table.insert(titles, v.title)
end

--[[ APIs ]]

--[[ GetShortcut(name) - Gets the shortcut by given key/shorthand or title

	Arguments.
	name - (string) The key/shorthand or title of the shortcut to retrieve.
	
	Returns.
	shortcut - (table) The shortcut.
	key - (string) The key of shortcut.
	index - (number) The index of shortcut.
	
]]
function lib:GetShortcut(name)
	local shortcut, index  = nil, 0
	if  name and type(name) == "string" then
		for i, v in ipairs(shortcuts) do
			if v.name == name or v.shorthand == name or v.title == name then
				shortcut = v
				index = i
				break
			end
		end
	end
	return shortcut, index
end

--[[ GetShortcutByIndex(index) - Gets the shortcut by given index

	Arguments.
	index - (number) The index of the shortcut to retrieve.
	
	Returns.
	shortcut - (table) The shortcut.
	key - (string) The key of shortcut.
	
]]
function lib:GetShortcutByIndex(index)
	local shortcut
	if  index and type(index) == "number" then
		shortcut = shortcuts[index]
	end
	return shortcut
end

--[[ NumOfShortcuts() - Gets the number of shortcuts available.

	Returns.
	num - (number) The number of shortcuts available.

]]
function lib:NumOfShortcuts()
	return num
end

--[[ IterateShortcuts() - Iterates through the shortcuts table. 

	Returns.
	iterator - (function) An iterator to traverse through the shortcuts table.
	
]]
function lib:IterateShortcuts()
	return ipairs(shortcuts)
end

--[[ GetShortcutsTitles() - Gets the titles of the shortcuts.
	
	Returns.
	titles - (table) The titles of the shortcuts.
	
]]
function lib:GetShortcutsTitles()
	return titles
end

--[[ RunShortcut(name) - Runs the shortcut.

	Arguments.
	name - (string) The key/shorthand  or title of the shortcut to run.
	
]]

local function hideUIPanel(self)
	if self.ignoreMenu then
		HideUIPanel(GameMenuFrame)
		self.ignoreMenu = nil
	end
end

function lib:RunShortcut(name)
	local shortcut = self:GetShortcut(name)
	if shortcut and shortcut.func and type(shortcut.func) == "function" then
		local playerLevel = UnitLevel("player");
		if shortcut.level and tonumber(shortcut.level) and playerLevel < shortcut.level  then
			print(CHARACTER_MSG:format(shortcut.title))
		elseif shortcut.frame then
			local inCombat = InCombatLockdown()
			local isRestricted = RestrictedFrames[shortcut.frame]
			if not isRestricted or not inCombat and isRestricted then
				local loadLoD = LoD_AddonFrames[shortcut.frame]
				
				if loadLoD then 
					loadLoD() 
				end
				
				local panel = UIPanelWindows[shortcut.frame]
				local centerFrame = not (GetUIPanel("left") or GetUIPanel("right") or GetUIPanel("doublewide")) and GetUIPanel("center")
				
				if panel and panel.area ~= "center" and centerFrame and centerFrame:IsShown() then
					HideUIPanel(centerFrame)
				end
				
				shortcut.func()
				
				if GameMenu_OptionFrames[shortcut.frame] then
					local frame = _G[shortcut.frame]
					if frame then 
						frame:HookScript("OnHide", hideUIPanel) 
					end
					GameMenu_OptionFrames[shortcut.frame] = nil
				end
			else
				print(COMBAT_MSG:format(shortcut.title))
			end
		else
			shortcut.func()
		end
	end
end
